#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QIntValidator>
#include "matrix.h"

/**
 * Matrix A, row * column, element (i,j)
 */
void print(const int *matrix, const int row, const int col) {
    int i,j;
    int offset;
    for (i = 0; i < row; i++) {
      offset = i * col;
      for (j = 0; j < col; j++) {
            printf("%d ", matrix[offset+j]);
        }
        printf("\n");
    }
}

/**
 * Matrix A, row * column, element (i,j)
 * A(i,j)m*s  B(i,j)s*n
 * C = A*B = C(i,j)m*n  c(i,j) = sum(a(i,k)*b(k,j))
 *
 * m1,m2 is input matrix, m1 is x*y, m2 is y*z
 * r is the result matrx, is x*z
 *
 * refer: https://zhuanlan.zhihu.com/p/108085571
 */
void matrix_multiply (int *m1, int *m2, int *r, int x, int y, int z ) {
    int *m1p;
    int *m2p;
    int offsetR=0;
    int k;
    int row;
    int column;

    for ( row = 0; row < x; row ++ ) {
        for ( column = 0; column < z; column ++ ) {
        m1p = m1 + row * y;
        m2p = m2 + column;
        r[offsetR] = 0;

        for ( k = 0; k < y; k ++) {
            r[offsetR] += *m1p * *m2p;
            m1p += 1;
            m2p += z;
        }
        offsetR++;
    // printf("---- M1*M2: %d, %d  %d \n", row, column, offsetR);
    // print(r, x,z);
        }
    }
    printf("M1: \n");
    print(m1,x,y);
    printf("M2: \n");
    print(m2,y, z);
    printf("M1*M2: \n");
    print(r, x,z);
}


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    // ui->txtInput->setValidator( new QIntValidator);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void matrix_plus (int *m1, int *m2, int *r, int x, int y ) {
    int *m1p;
    int *m2p;
    int *rp;
    int num;

    m1p = m1;
    m2p = m2;
    rp = r;


    for ( num = 0; num < (x*y); num++ ) {
        *rp = *m1p + *m2p;
        rp++;
        m1p++;
        m2p++;
    }
}

void matrix_minus (int *m1, int *m2, int *r, int x, int y ) {
    int *m1p;
    int *m2p;
    int *rp;
    int num;

    m1p = m1;
    m2p = m2;
    rp = r;


    for ( num = 0; num < (x*y); num++ ) {
        *rp = *m1p - *m2p;
        rp++;
        m1p++;
        m2p++;
    }
}

void matrix_nummult (int *m, int *r, int num, int x, int y ) {
    int *mp;
    int *rp;
    int cnt;

    mp = m;
    rp = r;

    for ( cnt = 0; cnt < (x*y); cnt++ ) {
        *r = *mp * num;
        r++;
        mp++;
    }
}

void t ( int *m, int *r, int x, int y ) {
    int i, j;
    int *mp, *rp;
    for ( i = 0; i < x; i++ ) {
        mp = m + i * y;
        rp = r + i;
        for ( j = 0; j < y; j++ ) {
            *rp = *mp;
            mp++;
            rp+=x;
        }
    }
}

void matrix_simplify ( int *m, int x, int y ) {
    int row, column1, column2;
    // int tmp[50] = {0}, i = 0;
    int *tmp,*tmpp;
    int *mp, *mp2;
    int flag = 0;
    int firstNum;
    int cnt = -1; /* cnt is the number of columns which don't contain 0 */
    for ( column1 = 0; column1 < y; column1++ ) {
        cnt++;

        /* check 0 in this column */
        for ( row = 0; row < x; row++ ) {
            mp = m + row*y;
            if ( *mp == 0 ) {
                mp += y;
            } else {
                firstNum = *mp;
                flag = 1;
                break;
            }
        }
        /* every number is 0, continue  */
        if ( flag == 0 ) {
            cnt--;
            continue;
        }
        /* queue */
        if ( row != cnt ) {
            tmp = (int*)malloc(sizeof(int)*y);
            tmpp = tmp;
            /* tmp stores numbers in the target row */
            for ( column2 = column1; column2 < y; column2++ ) {
                *tmpp = *mp;
                tmpp++;
                mp++;
            }
            /* target row stores numbers in the first row */
            mp2 = m + cnt * y;
            mp = m + row * y;
            for ( column2 = column1; column2 < y; column2++ ) {
                *mp = *mp2;
                mp++;
                mp2++;
            }
            /* first row stores numbers in tmp */
            mp2 = m + cnt * y;
            tmpp = tmp;
            for ( column2 = column1; column2 < y; column2++ ) {
                *mp2 = *tmpp;
                tmpp++;
                mp2++;
            }
            free(tmp);
            printf("-------------\n%d\n",*m);
        }
        /* simplify this row */
        mp = m + cnt * y;
        for ( column2 = column1; column2 < y; column2++ ) {    /* Bug output 1 for the second time, row1 turns    1    1*/
            (*mp) /= firstNum;
            mp++;
            printf("---------------------------\n%d\n",*m);
        }
        /* simplify other rows */
        flag = 0;
    }
}

void MainWindow::on_btnRun_clicked()
{
    QString str = ui->txtInput->toPlainText();
    Matrix a;
    a.create(str);
    QString strB = ui->txtInputB->toPlainText();
    Matrix b;
    b.create(strB);

    int curIdx = ui->cmbOper->currentIndex();
    if (curIdx == 0) {
        Matrix* c = a.plus(b);
        if (c != NULL) {
          QString result = c->dump();
          c->destory();
          delete c;
          // QMessageBox::about(this, "DUMP" , s);
          ui->txtResult->setText(result);
        } else {
            ui->txtResult->setText("BAD");
        }
    } else if (curIdx == 1) {
        if (a.rows == b.rows && a.cols == b.cols) {
            Matrix* c = new Matrix();
            c->rows = a.rows;
            c->cols = b.cols;
            qDebug() << " malloc c ";
            int size = c->rows * c->cols;
            // c->matrix = new int[size];
            c->matrix = (int*)malloc(sizeof(int) * size );
            // cal
            matrix_minus(a.matrix, b.matrix, c->matrix, a.rows, a.cols);
            // output
            qDebug() << " ----------------- ";
            QString result = c->dump();
            c->destory();
            delete c;
            ui->txtResult->setText(result);
        } else {
            QMessageBox::about(this, "BAD " , "not same rows and cols");
        }
    } else if (curIdx == 2) {
        Matrix* c = a.multiply(b);
        if (c != NULL) {
          QString result = c->dump();
          c->destory();
          delete c;
          // QMessageBox::about(this, "DUMP" , s);
          ui->txtResult->setText(result);
        } else {
            ui->txtResult->setText("BAD");
        }
    } else if (curIdx == 3) {
        int d = strB.toInt() ;
        // if ( d> ) {
            Matrix* c = new Matrix();
            c->rows = a.rows;
            c->cols = a.cols;
            qDebug() << " malloc c ";
            int size = c->rows * c->cols;
            // c->matrix = new int[size];
            c->matrix = (int*)malloc(sizeof(int) * size );
            // cal
            matrix_nummult (a.matrix, c->matrix,  d, a.rows, a.cols);
            // output
            qDebug() << " ----------------- ";
            QString result = c->dump();
            c->destory();
            delete c;
            ui->txtResult->setText(result);
        //} else {
        //    QMessageBox::about(this, "BAD " , "not same rows and cols");
        //}
    } else if (curIdx == 4) {
        int d = strB.toInt() ;
        if ( 1 ) {
            Matrix* c = new Matrix();
            c->rows = a.rows;
            c->cols = a.cols;
            qDebug() << " malloc c ";
            int size = c->rows * c->cols;
            // c->matrix = new int[size];
            c->matrix = (int*)malloc(sizeof(int) * size );
            // cal
            // void t (  );
            // output
            qDebug() << " ----------------- ";
            QString result = c->dump();
            c->destory();
            delete c;
            ui->txtResult->setText(result);
        } else {
            QMessageBox::about(this, "BAD " , "not same rows and cols");
        }
    } else {
        QString curOp = ui->cmbOper->currentText();
        QString msg = QString("Operator : %1 ,  %2 . \n").arg(curOp).arg(curIdx);
        QMessageBox::about(this, "TBD oper" , msg);
        ui->txtResult->setText("");
    }
}
